package org.ly817.test.reactor;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * Created by LuoYu on 2019/5/2.
 *
 * 简单reactor模式
 * prototype of reactor model
 */
public class Reactor implements Runnable {
    /**
     * 调用SPI 由操作系统底层实现
     */
    final Selector selector;

    final ServerSocketChannel serverSocketChannel;

    public Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        serverSocketChannel.configureBlocking(false);
        SelectionKey sk = serverSocketChannel.register(selector,
                SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
    }

    private class Acceptor implements Runnable{
        @Override
        public void run() {
            try {
                SocketChannel channel = serverSocketChannel.accept();
                if (channel != null) {
                    new Handler(selector,channel);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }



    /**
     * 分发
     */
    private void dispatch(SelectionKey sk){
        // 取出与这个事件绑定的acceptor，调用对应的handler处理逻辑
        Runnable r = (Runnable) sk.attachment();
        if (r != null) {
            // 简线程模式使用run，多线程模式使用线程池来执行Runnable
            // 这个线程值对应netty中的workEventLoopGroup，并且netty中的handler是继承Executor接口
            r.run();
        }
    }


    /**
     * 使用selector监听io事件
     * 获取io事件 进行分发
     * 对应netty中的bossEventLoopGroup
     */
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()){
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext()) {
                    dispatch((SelectionKey) it.next());
                    selected.clear();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private class Handler implements Runnable {
        final SocketChannel socketChannel;

        final SelectionKey sk;

        ByteBuffer input = ByteBuffer.allocate(1024);

        ByteBuffer output = ByteBuffer.allocate(1024);

        static final int READING = 0, SENDING = 1;

        int state = READING;

        public Handler(Selector selector, SocketChannel channel) throws IOException {
            socketChannel = channel;
            socketChannel.configureBlocking(false);
            sk = socketChannel.register(selector,0);
            sk.attach(this);
            sk.interestOps(SelectionKey.OP_READ);
            selector.wakeup();

        }

        @Override
        public void run() {
            try {
                if (state == READING) read();
                else if (state == SENDING) send();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void send() throws IOException {
            socketChannel.read(output);
            if (outputIsComplete()) {
                sk.cancel();
            }
        }

        private boolean outputIsComplete() {
            return false;
        }

        private void read() throws IOException {
            socketChannel.read(input);
            if (inputIsComplete()){
                process();
                state = SENDING;
            }
        }

        private void process() {

        }

        private boolean inputIsComplete() {
            return false;
        }


    }
}
